package cloud.seckill.seckillserver.common.redis;

import cn.hutool.core.text.replacer.StrReplacer;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.K;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static cloud.seckill.seckillserver.common.constant.RedisConstant.GOODS_KEY;
import static cloud.seckill.seckillserver.common.constant.RedisConstant.KEY_PREFIX_VALUE;

/**
 * @title: RedisUtil
 * @Author Wangsj
 * @Date: 2021/5/24
 */
@Component
@Slf4j
public class RedisUtil {


    @Resource
    private RedisTemplate<Serializable, Serializable> redisTemplate;


    public boolean setCacheLock(String lockKey, String value) throws InterruptedException {
        boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, value, 10L, TimeUnit.SECONDS);
        if (result) {
            log.debug("lock in value : {}", getCacheLock(lockKey));
            return result;
        } else {
            //通过一个自旋解决因线程竞争而导致的少卖
            //是否真正需要自旋？是否直接丢弃进程会好些，并发一高对服务器是否会有压力，需研究研究
            boolean oldValue;
            do {
                Integer restNum=Integer.parseInt(getValue(GOODS_KEY).toString());
                if (restNum==0){
                    log.info("没货了。。。");
                    break;
                }
                log.info("{} 等待等待", Thread.currentThread().getName());
                result = redisTemplate.opsForValue().setIfAbsent(lockKey, value, 10L, TimeUnit.SECONDS);
                log.info("{} 再试一次结果为 {}", Thread.currentThread().getName(), result);
            } while (!result);
            return result;
        }
    }

    public Object execute(DefaultRedisScript getRedisScript, List<Serializable> keys, Object... args) {
        return redisTemplate.execute(getRedisScript, keys, args);
    }

    public boolean removeCacheLock(String lockKey) {
        log.debug("unlock in value : {}", getCacheLock(lockKey));
        boolean result = redisTemplate.delete(lockKey);
        return result;
    }

    public Serializable getCacheLock(String lockKey) {
        return redisTemplate.opsForValue().get(lockKey);
    }

    /**
     * 缓存value操作
     *
     * @param k
     * @param v
     * @param time
     * @return
     */
    public boolean cacheValue(String k, Serializable v, long time) {
        String key = KEY_PREFIX_VALUE + k;
        try {
            ValueOperations<Serializable, Serializable> valueOps = redisTemplate.opsForValue();
            valueOps.set(key, v);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            log.error("缓存[{}]失败, value[{}]", key, v, t);
        }
        return false;
    }

    /**
     * 缓存value操作
     *
     * @param k
     * @param v
     * @param time
     * @param unit
     * @return boolean
     */
    public boolean cacheValue(String k, Serializable v, long time, TimeUnit unit) {
        String key = KEY_PREFIX_VALUE + k;
        try {
            ValueOperations<Serializable, Serializable> valueOps = redisTemplate.opsForValue();
            valueOps.set(key, v);
            if (time > 0) redisTemplate.expire(key, time, unit);
            return true;
        } catch (Throwable t) {
            log.error("缓存[{}]失败, value[{}]", key, v, t);
        }
        return false;
    }

    /**
     * 缓存value操作
     *
     * @param k
     * @param v
     * @return
     */
    public boolean cacheValue(String k, Serializable v) {
        return cacheValue(k, v, -1);
    }

    /**
     * 判断缓存是否存在
     *
     * @param k
     * @return
     */
    public boolean containsValueKey(String k) {
        String key = KEY_PREFIX_VALUE + k;
        try {
            return redisTemplate.hasKey(key);
        } catch (Throwable t) {
            log.error("判断缓存存在失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 获取缓存
     *
     * @param k
     * @return
     */
    public Serializable getValue(String k) {
        try {
            ValueOperations<Serializable, Serializable> valueOps = redisTemplate.opsForValue();
            return valueOps.get(KEY_PREFIX_VALUE + k);
        } catch (Throwable t) {
            log.error("获取缓存失败key[" + KEY_PREFIX_VALUE + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 移除缓存
     *
     * @param k
     * @return
     */
    public boolean removeValue(String k) {
        String key = KEY_PREFIX_VALUE + k;
        try {
            redisTemplate.delete(key);
            return true;
        } catch (Throwable t) {
            log.error("获取缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 递增
     *
     * @param k
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String k, long delta) {
        String key = KEY_PREFIX_VALUE + k;
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @param k     键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String k, long delta) {
        String key = KEY_PREFIX_VALUE + k;
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }


}
